package contable.aplicacion;

import contable.aplicacion.sql.MySQL;
import contable.dominio.Cardex;
import contable.dominio.Cuenta;
import contable.dominio.Empleado;
import contable.dominio.Inventario;
import contable.dominio.Periodo;
import contable.dominio.Planilla;
import contable.dominio.Registro;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Date;
import java.util.LinkedList;

/**
 * Se encarga de la persistencia de los objetos a la base de datos
 *
 * @author suncin
 */
public class FactoriaSQL {

    private MySQL sql;
    private static final FactoriaSQL INSTANCIA = new FactoriaSQL();

    private FactoriaSQL() {
        sql = MySQL.getInstancia();
    }

    /**
     * Obtener la instancia de FactoriaSQL
     *
     * @return La única instancia de FactoriaSQL
     */
    public static FactoriaSQL getINSTANCIA() {
        return INSTANCIA;
    }

    /**
     * Actualizar una cuenta a la base de datos
     *
     * @param c Cuenta a actualizar
     */
    public void commit(Cuenta c) {
        String consulta = "UPDATE Cuentas SET debe = ?, haber = ? WHERE codigo = ?";
        sql.actualizarRegistro(consulta, c.getDebe(), c.getHaber(), c.getCodigo());
    }

    /**
     * Recuperar una cuenta de la base de datos
     *
     * @param c Cuenta donde se almacenara los datos
     * @param cod Código de la cuenta
     */
    public void rollback(Cuenta c, String cod) {
        String consulta = "SELECT * FROM Cuentas WHERE codigo = ?";
        ResultSet rs = sql.consultarRegistro(consulta, cod);
        try {
            if (rs.next()) {
                c.setCodigo(rs.getString("codigo"));
                c.setNombre(rs.getString("nombre"));
                c.setRubro(rs.getInt("rubro"));
                c.abonar(rs.getDouble("haber"));
                c.cargar(rs.getDouble("debe"));
            }
        } catch (SQLException ex) {
            Notificaciones.notificarError(FactoriaSQL.class, "No se puede recuperar la cuenta " + cod, ex);
        }
    }

    /**
     * Actualizar un empleado a la base de datos
     *
     * @param emp empleado a actualizar en la base de datos
     */
    public void commit(Empleado emp) {
        String consulta = "UPDATE Empleados SET nombres = ?, apellidos = ?, fechaNac = ?, sexo = ?, NIT = ?, ISSS = ?, NUP = ?, cargo = ?, sueldo = ? WHERE DUI = ?";
        sql.actualizarRegistro(consulta, emp.getNombres(), emp.getApellidos(), emp.getFechaNacimiento(), String.valueOf(emp.getSexo()), emp.getNIT(), emp.getISSS(), emp.getNUP(), emp.getCargo(), emp.getSueldo(), emp.getDUI());
    }

    /**
     * Ingresar un empleado a la base de datos
     *
     * @param emp el empleado a insertar
     * @param dui el DUI del empleado
     */
    public void commit(Empleado emp, String dui) {
        String consulta = "INSERT INTO Empleados (nombres, apellidos, fechaNac, sexo, DUI, NIT, ISSS, NUP, cargo, sueldo) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
        sql.insertarRegistro(consulta, emp.getNombres(), emp.getApellidos(), emp.getFechaNacimiento(), String.valueOf(emp.getSexo()), dui, emp.getNIT(), emp.getISSS(), emp.getNUP(), emp.getCargo(), emp.getSueldo());
        consulta = "INSERT INTO Planillas SET Empleados_DUI = ?, Periodos_periodo = getPeriodo()";
        sql.insertarRegistro(consulta, dui);
    }

    /**
     * Recuperar un empleado de la base de datos
     *
     * @param emp Empleado donde se guardaran los datos
     * @param dui DUI del empleado a buscar
     */
    public void rollback(Empleado emp, String dui) {
        String consulta = "SELECT * FROM Empleados WHERE DUI = ?";
        ResultSet rs = sql.consultarRegistro(consulta, dui);
        try {
            if (rs.next()) {
                emp.setNombres(rs.getString("nombres"));
                emp.setApellidos(rs.getString("apellidos"));
                emp.setFechaNacimiento(rs.getDate("fechaNac"));
                emp.setSexo(rs.getString("sexo").charAt(0));
                emp.setDUI(rs.getString("DUI"));
                emp.setNIT(rs.getString("NIT"));
                emp.setISSS(rs.getString("ISSS"));
                emp.setNUP(rs.getString("NUP"));
                emp.setCargo(rs.getInt("cargo"));
                emp.setSueldo(rs.getDouble("sueldo"));
            }
        } catch (SQLException ex) {
            Notificaciones.notificarError(FactoriaSQL.class, "No se puede recuperar el empleado " + dui, ex);
        }
    }

    /**
     * Recuperar la lista de empleados de la base de datos
     *
     * @param emps Lista de empleados
     */
    public void rollback(LinkedList<Empleado> emps) {
        String consulta = "SELECT * FROM Empleados";
        ResultSet rs = sql.consultarRegistro(consulta);
        try {
            while (rs.next()) {
                Empleado aux = new Empleado();
                aux.setNombres(rs.getString("nombres"));
                aux.setApellidos(rs.getString("apellidos"));
                aux.setFechaNacimiento(rs.getDate("fechaNac"));
                aux.setSexo(rs.getString("sexo").charAt(0));
                aux.setDUI(rs.getString("DUI"));
                aux.setNIT(rs.getString("NIT"));
                aux.setISSS(rs.getString("ISSS"));
                aux.setNUP(rs.getString("NUP"));
                aux.setCargo(rs.getInt("cargo"));
                aux.setSueldo(rs.getDouble("sueldo"));
                if (!emps.contains(aux)) {
                    emps.add(aux);
                }
            }
        } catch (SQLException ex) {
            Notificaciones.notificarError(FactoriaSQL.class, "No se puede recuperar la lista de empleados", ex);
        }
    }

    /**
     * Ingresar un registro en la base de datos
     *
     * @param reg El nuevo registro contable
     */
    public void commit(Registro reg) {
        String consulta1 = "INSERT INTO Registros (fecha, descripcion, saldo, Periodos_Periodo) VALUES (?, ?, ?, ?)";
        sql.insertarRegistro(consulta1, reg.getFecha(), reg.getDescripcion(), reg.getSaldo(), reg.getPeriodo().getPeriodo());
        String consulta2 = "INSERT INTO Cuentas_has_Registros (Cuentas_codigo, Registros_fecha) VALUES (?, ?)";
        for (Cuenta c : reg.getCuentas()) {
            sql.insertarRegistro(consulta2, c.getCodigo(), reg.getFecha());
        }
        try {
            Thread.sleep(700);
        } catch (InterruptedException ex) {
            Notificaciones.notificarError(FactoriaSQL.class, ex);
        }
        Notificaciones.notificarRegistro(reg);
    }

    /**
     * Recuperar los registros hechos en el periodo contable de la base de datos
     *
     * @param p El periodo contable para el cual se quieren los registros
     * @param regs Lista con los registros del periodo solicitado
     */
    public void rollback(LinkedList<Registro> regs, Periodo p) {
        String consulta1 = "SELECT * FROM Registros WHERE Periodos_Periodo = ?";
        String consulta2;
        ResultSet rs = sql.consultarRegistro(consulta1, p.getPeriodo());
        try {
            while (rs.next()) {
                Date f = rs.getTimestamp("fecha");
                Registro tmp = new Registro(f, rs.getString("descripcion"), rs.getDouble("saldo"), p);
                consulta2 = "SELECT Cuentas_codigo FROM Cuentas_has_Registros WHERE Registros_fecha = ?";
                ResultSet rs2 = sql.consultarRegistro(consulta2, f);
                while (rs2.next()) {
                    Cuenta aux = new Cuenta();
                    rollback(aux, rs2.getString(1));
                    tmp.addCuenta(aux);
                }
                regs.add(tmp);
            }
        } catch (SQLException ex) {
            Notificaciones.notificarError(FactoriaSQL.class, "No se pueden recuperar los registros", ex);
        }
    }

    /**
     * Recuperar los registros hechos en un rango de la base de datos
     *
     * @param inicio La fecha en que empiezan los registros
     * @param fin La fecha en que finalizan los registros
     * @param regs Lista con los registros del rango solicitado
     */
    public void rollback(LinkedList<Registro> regs, Date inicio, Date fin) {
        String consulta1 = "SELECT * FROM Registros WHERE fecha BETWEEN ? AND ?";
        String consulta2;
        ResultSet rs = sql.consultarRegistro(consulta1, inicio, fin);
        try {
            while (rs.next()) {
                Date f = rs.getTimestamp("fecha");
                Registro tmp = new Registro(f, rs.getString("descripcion"), rs.getDouble("saldo"), null);
                consulta2 = "SELECT Cuentas_codigo FROM Cuentas_has_Registros WHERE Registros_fecha = ?";
                ResultSet rs2 = sql.consultarRegistro(consulta2, f);
                while (rs2.next()) {
                    Cuenta aux = new Cuenta();
                    rollback(aux, rs2.getString(1));
                    tmp.addCuenta(aux);
                }
                regs.add(tmp);
            }
        } catch (SQLException ex) {
            Notificaciones.notificarError(FactoriaSQL.class, "No se pueden recuperar los registros", ex);
        }
    }

    /**
     * Ingresar un nuevo inventario en la base de datos
     *
     * @param inv El nuevo inventario de producto o insumos
     * @param prod El producto o insumo en el inventario
     */
    public void commit(Inventario inv, int prod) {
        String consulta = "INSERT INTO Inventarios SET producto = ?, cantidad = ?, monto = ?, costo = ?, precio = ?";
        sql.insertarRegistro(consulta, prod, inv.getCantidad(), inv.getMonto(), inv.getCosto(), inv.getPrecio());
    }

    /**
     * Actualizar un inventario en la base de datos
     *
     * @param inv Inventario de un producto o insumo
     */
    public void commit(Inventario inv) {
        String consulta = "UPDATE Inventarios SET precio = ?, costo = ?, cantidad = ?, monto = ? WHERE producto = ?";
        sql.actualizarRegistro(consulta, inv.getPrecio(), inv.getCosto(), inv.getCantidad(), inv.getMonto(), inv.getProducto());
    }

    /**
     * Recuperar un inventario de la base de datos
     *
     * @param inv Inventario donde se almacenaran los datos
     * @param prod Código del producto o insumo
     */
    public void rollback(Inventario inv, int prod) {
        String consulta = "SELECT * FROM Inventarios WHERE producto = ?";
        ResultSet rs = sql.consultarRegistro(consulta, prod);
        try {
            if (rs.next()) {
                inv.setProducto(rs.getInt("producto"));
                inv.setPrecio(rs.getDouble("precio"));
                inv.setCosto(rs.getDouble("costo"));
                inv.setCantidad(rs.getInt("cantidad"));
                inv.setMonto();
            }
        } catch (SQLException ex) {
            Notificaciones.notificarError(FactoriaSQL.class, "No se puede recuperar el inventario " + prod, ex);
        }
    }

    /**
     * Insertar un detalle cardex en la base de datos
     *
     * @param cdx Detalle cardex de un inventario
     */
    public void commit(Cardex cdx) {
        String consulta = "INSERT INTO Cardex SET fecha = ?, entrada = ?, cantidad = ?, costo = ?, monto = ?, Periodos_periodo = ?, Inventarios_producto = ?";
        sql.insertarRegistro(consulta, cdx.getFecha(), cdx.esIngreso(), cdx.getCantidad(), cdx.getCosto(), cdx.getMonto(), cdx.getPeriodo().getPeriodo(), cdx.getProducto().getProducto());
        Notificaciones.notificarCardex(cdx);
    }

    /**
     * Recuperar los detalles de cardex de un inventario hechos en el periodo contable de la base
     * de datos
     *
     * @param p El periodo contable para el cual se requieren los registros
     * @param cdxs Lista con los detalle cardex a solicitar
     * @param inv El inventario del producto para los detalle cardex
     */
    public void rollback(Periodo p, LinkedList<Cardex> cdxs, Inventario inv) {
        String consulta = "SELECT * FROM Cardex WHERE Inventarios_producto = ? AND Periodos_periodo = ?";
        ResultSet rs = sql.consultarRegistro(consulta, inv.getProducto(), p.getPeriodo());
        try {
            while (rs.next()) {
                Cardex tmp = new Cardex(inv);
                tmp.setFecha(rs.getTimestamp("fecha"));
                tmp.esCompra(rs.getBoolean("entrada"));
                tmp.setCantidad(rs.getInt("cantidad"));
                tmp.setCosto(rs.getDouble("costo"));
                tmp.setMonto();
                tmp.setPeriodo(p);
                cdxs.add(tmp);
            }
        } catch (SQLException ex) {
            Notificaciones.notificarError(FactoriaSQL.class, "No se pueden recuperar los cardex", ex);
        }
    }

    /**
     * Recuperar los detalles de cardex hechos en un rango de tiempo de la base
     * de datos
     *
     * @param inicio La fecha en que empiezan los detalle cardex
     * @param fin La fecha en que terminan los detalle cardex
     * @param cdxs Lista con los detalle cardex a solicitar
     * @param inv El inventario del producto para los detalle cardex
     */
    public void rollback(Date inicio, Date fin, LinkedList<Cardex> cdxs, Inventario inv) {
        String consulta = "SELECT * FROM Cardex WHERE Inventarios_producto = ? AND fecha BETWEEN ? AND ?";
        ResultSet rs = sql.consultarRegistro(consulta, inicio, fin);
        try {
            while (rs.next()) {
                Cardex tmp = new Cardex(inv);
                tmp.setFecha(rs.getTimestamp("fecha"));
                tmp.esCompra(rs.getBoolean("entrada"));
                tmp.setCantidad(rs.getInt("cantidad"));
                tmp.setCosto(rs.getDouble("costo"));
                tmp.setMonto();
                cdxs.add(tmp);
            }
        } catch (SQLException ex) {
            Notificaciones.notificarError(FactoriaSQL.class, "No se pueden recuperar los cardex", ex);
        }
    }

    /**
     * Ingresar un periodo contable a la base de datos
     *
     * @param p El periodo contable a iniciar
     * @param inicio La fecha y hora en que inicia el periodo contable
     */
    public void commit(Periodo p, Date inicio) {
        String consulta = "INSERT INTO Periodos SET inicio = ?";
        sql.insertarRegistro(consulta, inicio);
    }

    /**
     * Actualizar un periodo contable a la base de datos
     *
     * @param p El periodo contable a cerrar
     */
    public void commit(Periodo p) {
        String consulta = "UPDATE Periodos SET fin = ? WHERE periodo = ?";
        sql.actualizarRegistro(consulta, p.getFin(), p.getPeriodo());
    }

    /**
     * Recuperar un determinado periodo de la base de datos
     *
     * @param p El periodo donde se almacenaran los datos
     * @param periodo El numero de periodo
     */
    public void rollback(Periodo p, int periodo) {
        String consulta = "SELECT * FROM Periodos WHERE periodo = ?";
        ResultSet rs = sql.consultarRegistro(consulta, periodo);
        try {
            if (rs.next()) {
                p.setPeriodo(rs.getInt("periodo"), rs.getTimestamp("inicio"), rs.getTimestamp("fin"));
            }
        } catch (SQLException ex) {
            Notificaciones.notificarError(FactoriaSQL.class, "No se puede recuperar el periodo: " + periodo, ex);
        }
    }

    /**
     * Recuperar el ultimo periodo de la base de datos
     *
     * @param p El periodo donde se almacenaran los datos
     */
    public void rollback(Periodo p) {
        String consulta = "SELECT periodo, inicio FROM Periodos WHERE periodo = getPeriodo()";
        int count = sql.contarRegistros("SELECT COUNT(*) FROM Periodos");
        if (count > 0) {
            ResultSet rs = sql.consultarRegistro(consulta);
            try {
                if (rs.next()) {
                    p.setPeriodo(rs.getInt("periodo"), rs.getTimestamp("inicio"), null);
                }
            } catch (SQLException ex) {
                Notificaciones.notificarError(FactoriaSQL.class, "No se puede recuperar el periodo", ex);
            }
        }
    }

    /**
     * Ingresar un detalle de planilla a la base de datos
     *
     * @param pl El detalle de la planilla
     * @param emp El empleado al que se le calcula el salario
     */
    public void commit(Planilla pl, Empleado emp) {
        String consulta = "INSERT INTO Planillas (Empleados_DUI, Periodos_periodo, horas_lab, horas_extras, horas_noct, incapacidad, salario) VALUES (?, ?, ?, ?, ?, ?, ?)";
        sql.insertarRegistro(consulta, emp.getDUI(), pl.getPeriodo().getPeriodo(), pl.getHorasLab(), pl.getHorasExtra(), pl.getHorasNoct(), pl.getIncapacidad(), pl.calcularSalario());
    }

    /**
     * Actualizar un detalle de la planilla en la base de datos
     *
     * @param pl El detalle de la planilla a actualizar
     */
    public void commit(Planilla pl) {
        String consulta = "UPDATE Planillas SET horas_lab = ?, horas_extras = ?, horas_noct = ?, incapacidad = ?, salario = ? WHERE Empleados_DUI = ? AND Periodos_periodo = ?";
        sql.actualizarRegistro(consulta, pl.getHorasLab(), pl.getHorasExtra(), pl.getHorasNoct(), pl.getIncapacidad(), pl.calcularSalario(), pl.getEmpleado().getDUI(), pl.getPeriodo().getPeriodo());
    }

    /**
     * Recuperar un detalle de planilla para un empleado en el periodo actual de
     * la base de datos
     *
     * @param pl El detalle de planilla para el periodo actual
     * @param emp El empleado para el cual se quiere la planilla
     */
    public void rollback(Planilla pl, Empleado emp) {
        Periodo p = new Periodo();
        String consulta = "SELECT * FROM Planillas WHERE Empleados_DUI = ? AND Periodos_periodo = ?";
        ResultSet rs = sql.consultarRegistro(consulta, emp.getDUI(), p.getPeriodo());
        try {
            if (rs.next()) {
                pl.setEmpleado(emp);
                pl.setPeriodo(p);
                pl.setHorasLab(rs.getDouble("horas_lab"));
                pl.setHorasExtra(rs.getDouble("horas_extras"));
                pl.setHorasNoct(rs.getDouble("horas_noct"));
                pl.setIncapacidad(rs.getInt("incapacidad"));
            }
        } catch (SQLException ex) {
            Notificaciones.notificarError(FactoriaSQL.class, "No se pudo recuperar el detalle de planilla del empleado " + emp.getDUI(), ex);
        }
    }
}
